-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Memory lru #695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory lru #695
Conversation
* limitations under the License. | ||
*/ | ||
|
||
#import <XCTest/XCTest.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: separate framework imports from the rest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
- (void)testPickSequenceNumberPercentile { | ||
const int numTestCases = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note you can define a struct locally here within this function and initialize it just as you've done. Something like this would allow you to avoid the fiddly indexes in the code below.
struct Case {
int queries;
int expected;
};
Case testCases[numTestCases] = {{0, 0}, {10, 1}, {9, 0}, {50, 5}, {49, 4}};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I didn't know you could just define structs wherever. I definitely like that better.
@@ -0,0 +1,101 @@ | |||
#import "Firestore/Source/Local/FSTLRUGarbageCollector.h" | |||
#import <queue> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newline before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
class RollingSequenceNumberBuffer { | ||
public: | ||
RollingSequenceNumberBuffer(NSUInteger max_elements) : max_elements_(max_elements) { | ||
queue_ = std::make_unique<priority_queue<FSTListenSequenceNumber> >(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're not letting this priority queue escape or otherwise be shared you don't need to heap allocate it.
Just make the member
std::priority_queue<FSTListenceSequenceNumber> queue_;
Then you can initialize it exactly as you have max_elements_
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. In the future, I think I can even stack allocate the backing vector, since I know the max size at constructor time.
|
||
const FSTListenSequenceNumber kFSTListenSequenceNumberInvalid = -1; | ||
|
||
using std::priority_queue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm kind of meh on using
statements for members of std
. If you're going to use them, use them consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, removed the using
.
queue_ = std::make_unique<priority_queue<FSTListenSequenceNumber> >(); | ||
} | ||
|
||
RollingSequenceNumberBuffer(const RollingSequenceNumberBuffer &other) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You only need to define one copy constructor and assignment operator as deleted to suppress the other.
You can prove it to yourself with a snippet like the following (though please delete after):
static_assert(!std::is_move_constructable<RollingSequenceNumberBuffer>::type, "no moves");
static_assert(!std::is_move_assignable<RollingSequenceNumberBuffer>::type, "no move assignments");
static_assert(!std::is_copy_constructable<RollingSequenceNumberBuffer>::type, "no copies");
static_assert(!std::is_copy_assignable<RollingSequenceNumberBuffer>::type, "no copy assignments");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the recommended fashion, I was just copying what I saw elsewhere. Any difference / preference for which one of each is kept?
For reference: https://github.com/firebase/firebase-ios-sdk/blob/master/Firestore/core/src/firebase/firestore/remote/datastore.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do what I say, not what I do :-P.
@@ -418,6 +421,8 @@ - (void)releaseQuery:(FSTQuery *)query { | |||
FSTAssert(queryData, @"Tried to release nonexistent query: %@", query); | |||
|
|||
[self.localViewReferences removeReferencesForID:queryData.targetID]; | |||
// TODO(gsoltis): kill this if statement, give GC a reference to query cache, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The definitely sounds right.
@@ -0,0 +1,29 @@ | |||
#import <Foundation/Foundation.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing copyright (and that's why Travis failed your build).
|
||
@end | ||
|
||
@implementation FSTLRUGarbageCollector { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The empty block here is unnecessary.
|
||
const FSTListenSequenceNumber kFSTListenSequenceNumberInvalid = -1; | ||
|
||
class RollingSequenceNumberBuffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment on the purpose of the class.
In particular it's helpful to understand why a priority queue that efficiently tracks the highest seen sequence number is helpful. I think it's because you're looking for the oldest (i.e. smallest) max_elements
number of sequence numbers, right?
: max_elements_(max_elements), queue_(std::priority_queue<FSTListenSequenceNumber>()) { | ||
} | ||
|
||
RollingSequenceNumberBuffer(const RollingSequenceNumberBuffer &other) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you declare any copy constructor/assignment that implicitly precludes the move operations from being generated. The deletions of the moves are redundant.
https://stackoverflow.com/questions/33096653/make-a-class-non-copyable-and-non-movable
@@ -0,0 +1,100 @@ | |||
#import "Firestore/Source/Local/FSTLRUGarbageCollector.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copyright 2018
} | ||
} | ||
}]; | ||
[self.queries removeObjectsForKeys:toRemove]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also need to remove document keys from the mutated document sequence numbers?
@@ -98,6 +98,18 @@ NS_ASSUME_NONNULL_BEGIN | |||
/** Removes the cached entry for the given query data (no-op if no entry exists). */ | |||
- (void)removeQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group; | |||
|
|||
- (void)enumerateSequenceNumbersUsingBlock:(void (^)(FSTListenSequenceNumber sequenceNumber, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should note that it's not obligated to return sequence numbers in order.
- (void)enumerateSequenceNumbersUsingBlock:(void (^)(FSTListenSequenceNumber sequenceNumber, | ||
BOOL *stop))block; | ||
|
||
- (NSUInteger)removeQueriesThroughSequenceNumber:(FSTListenSequenceNumber)sequenceNumber |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments. Removes queries with sequence numbers up to and including the given sequence number.
What does live queries have to do with it? Are they excluded? Does encountering a live query terminate the removal?
NSMutableArray<FSTQuery *> *toRemove = [NSMutableArray array]; | ||
[self.queries | ||
enumerateKeysAndObjectsUsingBlock:^(FSTQuery *query, FSTQueryData *queryData, BOOL *stop) { | ||
if (queryData.sequenceNumber <= sequenceNumber) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this be a compound condition separated by &&
?
group:(__unused FSTWriteGroup *)group { | ||
NSMutableArray<FSTQuery *> *toRemove = [NSMutableArray array]; | ||
[self.queries | ||
enumerateKeysAndObjectsUsingBlock:^(FSTQuery *query, FSTQueryData *queryData, BOOL *stop) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can avoid this second scan if your priority were to track (sequence number, target id) pairs. Order by the first (which yields the same sequence numbers) but then once you're done accumulating the queue you have all the primary keys of the targets to delete in hand.
You'll need to store something else to indicate it was a mutated document key instead; not sure if this is required (see other questions).
Sorry, this wasn't intended to be reviewed yet. I'll assign someone when it is. I'd like to get a review of another PR first before tackling this one. I will hopefully have that one up today or tomorrow. |
@gsoltis is this PR abandoned? If so, we should close it. |
Yup, a different PR was merged that included this. Closing. |
Do Not Merge.
This PR shows an implementation of FSTLRUGarbageCollector. The functioning of that GC strategy requires a few changes to FSTQueryCache.h, which are shown here and implemented for FSTMemoryQueryCache.
Tests are provided for various steps in the process of running the LRU GC, however the GC is not wired into the Local Store and will not yet be triggered. I expect that the tests will run against LevelDB persistence which minimal modifications once I have implemented the necessary pieces in FSTLevelDBQueryCache and any other necessary places.
There are two notable changes beyond the LRU implementation and associated query cache changes:
Not yet implemented: